﻿namespace wToggle.Platform
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net.Http;
    using System.Threading.Tasks;

    using wToggle.Platform.Data;
    using wToggle.Platform.Interfaces;

    using XPlatUtils;

    public class TagProvider : ITagProvider
    {
        public async Task<ICollection<Tag>> GetTags(ulong workspaceId)
        {
            if (workspaceId <= 0)
            {
                throw new ArgumentOutOfRangeException("workspaceId", "Invalid workspace ID specified");
            }

            var cached = CacheLayer.LoadEntities<Tag>();

            if (cached != null)
            {
                var list = cached.Where(t => t.WorkspaceId == workspaceId).ToList();
                if (list.Any())
                {
                    return list;
                }
            }

            var auth = ServiceContainer.Resolve<ITogglAuth>();

            using (var client = auth.GetPreauthenticatedClient())
            {
                var tags = await client.GetJsonAsync<List<Tag>>("workspaces/" + workspaceId + "/tags");

                if (tags == null)
                {
                    return new List<Tag>();
                }

                var list = tags.OrderBy(t => t.Name).ToList();

                await CacheLayer.StoreEntities(list);
                return list;
            }
        }

        public async Task<Tag> GetTag(ulong id, ulong workspaceId)
        {
            if (id <= 0)
            {
                throw new ArgumentOutOfRangeException("id", "Invalid tag ID specified");
            }

            var cached = CacheLayer.LoadEntity<Tag>(id);

            if (cached != null)
            {
                return cached;
            }

            var tags = await this.GetTags(workspaceId) ?? new List<Tag>();

            return tags.FirstOrDefault(t => t.Id == id);
        }

        public async Task<Tag> Create(Tag tag, bool withLock = true)
        {
            if (tag == null)
            {
                throw new ArgumentNullException("tag");
            }

            if (Toggly.HasInternetAccess())
            {
                var auth = ServiceContainer.Resolve<ITogglAuth>();

                using (var client = auth.GetPreauthenticatedClient())
                {
                    var result =
                        await
                        client.PostJsonAsync<ResponseData<Tag>, TagRequest>(
                            "tags",
                            new TagRequest { Data = tag });

                    if (result == null)
                    {
                        return null;
                    }

                    tag.Id = result.Data.Id;
                    await CacheLayer.StoreEntity(result.Data, withLock);

                    return result.Data;
                }
            }
            else
            {
                await CacheLayer.StoreEntryWrite(tag, WriteType.Create, withLock);
                return tag;
            }
        }

        public async Task<Tag> Update(Tag tag, bool withLock = true)
        {
            if (tag == null)
            {
                throw new ArgumentNullException("tag");
            }

            if (tag.Id <= 0)
            {
                throw new ArgumentException("Tag ID out of range");
            }

            if (Toggly.HasInternetAccess())
            {
                var auth = ServiceContainer.Resolve<ITogglAuth>();

                using (var c = auth.GetPreauthenticatedClient())
                {
                    var result =
                        await
                        c.PutJsonAsync<ResponseData<Tag>, TagRequest>(
                            "tags/" + tag.Id,
                            new TagRequest { Data = tag });

                    if (result == null)
                    {
                        return null;
                    }

                    await CacheLayer.StoreEntity(result.Data, withLock);

                    return result.Data;
                }
            }
            else
            {
                await CacheLayer.StoreEntryWrite(tag, WriteType.Update, withLock);
                return tag;
            }
        }

        public async Task<bool> Delete(ulong id, bool withLock = true)
        {
            if (Toggly.HasInternetAccess())
            {
                var auth = ServiceContainer.Resolve<ITogglAuth>();

                using (var client = auth.GetPreauthenticatedClient())
                {
                    var result = await client.DeleteAsync("tags/" + id);

                    try
                    {
                        result.EnsureSuccessStatusCode();

                        await CacheLayer.RemoveEntity<Tag>(id, withLock);

                        return true;
                    }
                    catch (HttpRequestException e)
                    {
                        return false;
                    }
                }
            }
            else
            {
                var existing = CacheLayer.LoadEntity<Tag>(id);

                if (existing == null)
                {
                    return true;
                }

                await CacheLayer.StoreEntryWrite(existing, WriteType.Delete, withLock);
                return true;
            }
        }
    }
}